DearMiku

OpenGL ES on iOS --- 光照进阶

字数统计: 1.4k阅读时长: 5 min
2017/12/06 Share

简述

本文记录我记录我学习 坐标体系和矩阵转换的过程,加深学习便于后续查询,可能有些描述不够准确,或者内容不够充实,还请多多指正,共同学习.

光源分类

在基础光照时,学习了光照对物体的作用,也就相当于物体的材质,这次主要说 现实生活中的光源

平行光

当光源无限远时,从其发射过来的的光可以近似的看做平行光(例如太阳);这时 光线的方向都是一致的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
float ambientStrength = 0.3;    //环境因子
float specularStrength = 2.0;
float reflectance = 256.0;

//平行光方向
vec3 paraLightDir = normalize(vec3(-0.2,-1.0,-0.3));

//环境光
vec3 ambient = ambientStrength * texture(Texture,outTexCoord).rgb;

//漫反射
vec3 norm = normalize(outNormal);
vec3 lightDir = normalize(lightPo - FragPo); //当前顶点 至 光源的的单位向量
float diff = max(dot(norm,paraLightDir),0.0);
vec3 diffuse = diff * lightColor*texture(Texture,outTexCoord).rgb;

//镜面反射
vec3 viewDir = normalize(viewPo - FragPo);
vec3 reflectDir = reflect(-paraLightDir,outNormal);
float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
vec3 specular = specularStrength * spec * texture(specularTexture,outTexCoord).rgb;

//光线衰弱
float constantPara = 1.0f;
float linearPara = 0.09f;
float quadraticPara = 0.032f;
float LFDistance = length(lightPo - FragPo);
float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));

vec3 res = ambient + diffuse + specular;

FragColor = vec4(res,1.0);

点光源

点光源就是比较正常的光源,光从光源四散发出,光线的向量就等于光源到物体的向量.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
float ambientStrength = 0.3;    //环境因子
float specularStrength = 2.0;
float reflectance = 256.0;

float constantPara = 1.0f; //常亮
float linearPara = 0.09f; //线性部分因数
float quadraticPara = 0.032f; //二次项部分因数

//环境光
vec3 ambient = ambientStrength * texture(Texture,outTexCoord).rgb;

//漫反射
vec3 norm = normalize(outNormal);
vec3 lightDir = normalize(lightPo - FragPo); //当前顶点 至 光源的的单位向量

//点光源
float diff = max(dot(norm,lightDir),0.0); //光源与法线夹角
vec3 diffuse = diff * lightColor*texture(Texture,outTexCoord).rgb;

//镜面反射
vec3 viewDir = normalize(viewPo - FragPo);
vec3 reflectDir = reflect(-lightDir,outNormal);

float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
vec3 specular = specularStrength * spec * texture(specularTexture,outTexCoord).rgb;

float LFDistance = length(lightPo - FragPo);
float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));

vec3 res = (ambient + diffuse + specular)*lightWeakPara;

FragColor = vec4(res,1.0);

聚光源

聚光源的效果就相当于 手电筒,好比朝向指定范围的点光源~

在使用聚光源时,就需要指定 聚光朝向SpotDir,和切光角ϕ. 当光源指向点的向量和SpotDir的夹角大于ϕ时,则无法被光源照射到.

但是这样的明暗边界十分明显,效果不够真实

初始版本

这时,我们就需要将过渡边缘平滑,这时 我们就需要引入两个参数, 内锥角和外锥角. 外锥角就是切光角,而内锥角以内不需要平滑效果, 内锥角和外锥角之间需要平滑过度.
代码如下

1
2
3
4
5
6
7
8
9
10
11
/(一些复杂的计算操作 应该让CPU做,提高效率,不变的量也建议外部传输,避免重复计算)
float inCutOff = cos(radians(10.0f)); //内锥角cos值
float outCutOff = cos(radians(15.0f)); //外锥角cos值
vec3 spotDir = vec3(-1.2f,-1.0f,-2.0f); //聚光朝向

float theta = dot(lightDir,normalize(-spotDir)); //光源指向物体的向量 和 聚光朝向的 cos值
float epsilon = inCutOff - outCutOff; //内外锥角cos差值

//clamp(a,b,c);若b<a<c 则函数返回值为a 若不是,则返回值最小为b 最大为c
// (theta - outCutOff)/epsilon 若theta的角度小于内锥角 则其值>=1 若theta的角度大于外锥角 则其值<=0 这样光线就在内外锥角之间平滑变化.
float intensity = clamp((theta - outCutOff)/epsilon,0.0,1.0);

结果:
结果

光线衰弱

在现实情况中,光源发出的光线是会随着距离的增长而衰减的, 而且也不是线性衰减的,表现为在距离光源近的这段距离衰减的较快, 在距离光源较远的情况下衰减较慢.
通常使用这个公式来模拟光线衰减.

衰减公式

常数项通常保持为1.0,它的主要作用是保证分母永远不会比1小,否则的话在某些距离上它反而会增加强度,这肯定不是我们想要的效果
一次项会与距离值相乘,以线性的方式减少强度
二次项会与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候它就会比一次项更大了

效果展示
在效果图中,在达到距离100时就近似没有光的效果了.

参数距离表
距离与参数的关联.

1
2
3
4
float LFDistance = length(lightPo - FragPo);
float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));

vec3 res = (ambient + diffuse + specular)*lightWeakPara;

光照贴图

在一张纹理图中,由于材质不同,所呈现的效果也会有所不同,如同下面这个箱子,金属边框和木头在相同光源下所呈现的1效果肯定有所不同.
-w250

这时为了在显示光照效果时将其区分开来,则需要引入光照贴图的概念~ 如下图
-w250
在该贴图中,对应木头部分为黑色vec3(0.0); 而在金属边框部分 则对应的为灰色, 这样在计算 漫反射或者镜面时,将其作为参考系数,则可以让其呈现不同的效果.

1
2
3
4
5
6
7
vec3 spe = texture(specularTexture,outTexCoord).rgb;   //获取镜面光照贴图

vec3 viewDir = normalize(viewPo - FragPo);
vec3 reflectDir = reflect(-lightDir,outNormal);

float spec = pow(max(dot(viewDir, reflectDir),0.0),spL.reflectance);
vec3 specular = point_specularStrength * spec * spe; //使用光照贴图纹理

镜面光照纹理效果
是可以看出箱子铁框的镜面效果 比 木头的效果要强

CATALOG
  1. 1. 简述
  2. 2. 光源分类
    1. 2.1. 平行光
    2. 2.2. 点光源
    3. 2.3. 聚光源
  3. 3. 光线衰弱
  4. 4. 光照贴图